from pycalc import Game, Operation, OperationType, GameRules, STANDARD_RULES, ALL_RULES, Player


def add_temp_players(game: Game, players_count: int, ammo: int):
    """测试函数"""
    players_name = list("ABCDEFGHIJKLMNOPQRSTUVWXYZ")[:players_count]
    for name in players_name:
        game.add_player(name)
    for _ in range(ammo):
        for name in players_name:
            game.add_operation(name, Operation(OperationType.LOAD))
        game.step()


def step_with_string(game: Game, string: str):
    def split_string(s, n):
        return [s[i:i + n] for i in range(0, len(s), n)]

    splited = string.split("/")
    for operation in splited:
        name = operation[0]
        operation_char = operation[1]
        if operation_char == "+":
            game.add_operation(name, Operation(OperationType.LOAD))
        elif operation_char == "O":
            game.add_operation(name, Operation(OperationType.DEFENCE))
        elif operation_char == "-":
            game.add_operation(name, Operation(OperationType.TAUNT))
        elif operation_char == "[":
            game.add_operation(name, Operation(OperationType.TAUNT_FAKE_5))
        elif operation_char == "]":
            game.add_operation(name, Operation(OperationType.TAUNT_YEAH))
        else:
            attack_operations = split_string(operation[1:], 2)
            attacks = {}
            for attack_operation in attack_operations:
                target_player = game.find_player_with_name(attack_operation[0])
                target_ammo = int(attack_operation[1])
                attacks[target_player] = target_ammo
            game.add_operation(name, Operation(OperationType.ATTACK, attacks))
    game.step()
    return game.export_game_state()


def test():
    """测试函数"""
    test_cases = [
        {
            "name": "基础开枪",
            "players": 2,
            "initial_ammo": 20,
            "operations": "AB1/B+",
            "expected_survivors": ["A"],
            "description": "期望：A活B死 战斗应为：(A1, B) 原因：直接伤害"
        },
        {
            "name": "基础防御",
            "players": 2,
            "initial_ammo": 20,
            "operations": "AB1/BO",
            "expected_survivors": ["A", "B"],
            "description": "期望：都活 战斗应为：(A1, B防) 原因：防御防住单枪"
        },
        {
            "name": "基础双破防",
            "players": 2,
            "initial_ammo": 20,
            "operations": "AB2/BO",
            "expected_survivors": ["A"],
            "description": "期望：A活B死 战斗应为：(A2, B防) 原因：双弹破防"
        },
        {
            "name": "基础2v1",
            "players": 2,
            "initial_ammo": 20,
            "operations": "AB2/BA1",
            "expected_survivors": ["B"],
            "description": "期望：B活A死 战斗应为：(A2, B1) 原因：双打单双死"
        },
        {
            "name": "基础互打",
            "players": 2,
            "initial_ammo": 20,
            "operations": "AB1/BA1",
            "expected_survivors": [],
            "description": "期望：都死 战斗应为：(A1, B1) 原因：互打都死"
        },
        {
            "name": "基础核弹",
            "players": 2,
            "initial_ammo": 20,
            "operations": "AB3/BA1",
            "expected_survivors": ["A"],
            "description": "期望：A活B死 战斗应为：(A3, B1) 原因：核弹免疫对方"
        },
        {
            "name": "基础子铭战法（打自己2v1）",
            "players": 2,
            "initial_ammo": 20,
            "operations": "AA1B1/BA1",
            "expected_survivors": ["A"],
            "description": "期望：A活B死 战斗应为：(A1, A'B2) 原因：打自己的结果如果打自己的那发死，移除打自己的那发，否则与原来的相同"
        },
        {
            "name": "打自己炸膛（4发）",
            "players": 2,
            "initial_ammo": 20,
            "operations": "AA3/BA1",
            "expected_survivors": ["A"],
            "description": "期望：A活B死 战斗应为：(A, A'B4) 原因：同上"
        },
        {
            "name": "基础炸膛（4发）",
            "players": 3,
            "initial_ammo": 20,
            "operations": "A+/BA3/CA1",
            "expected_survivors": ["A"],
            "description": "期望：A活BC死 战斗应为：(A, BC4) 原因：4发炸膛攻击者死"
        },
        {
            "name": "基础多目标（完美分配）",
            "players": 3,
            "initial_ammo": 20,
            "operations": "AB2C1/BO/C+",
            "expected_survivors": ["A"],
            "description": "期望：A活BC死 战斗应为：(A2, B防), (A1, C) 原因：多目标相当于多个战斗"
        },
        {
            "name": "子弹合并2v1",
            "players": 3,
            "initial_ammo": 20,
            "operations": "AB1/BA1/CA1",
            "expected_survivors": ["A"],
            "description": "期望：A活BC死 战斗应为：(A1, BC2) 原因：同基础2v1"
        },
        {
            "name": "基础炸膛（5发）",
            "players": 3,
            "initial_ammo": 20,
            "operations": "AA3/B+/CA2",
            "expected_survivors": ["A", "C"],
            "description": "期望：AC活B死 战斗应为：(A, A'C5) 原因：5发炸膛除了攻击者死，攻击者无敌"
        },
        {
            "name": "基础炸膛（6发）",
            "players": 3,
            "initial_ammo": 20,
            "operations": "AA3/B+/CA3",
            "expected_survivors": [],
            "description": "期望：都死 战斗应为：(A, A'C6) 原因：6发都死"
        },
        {
            "name": "基础炸膛（10发）",
            "players": 5,
            "initial_ammo": 20,
            "operations": "AA3/BA3/CA3/DA1/EE1",
            "expected_survivors": ["A", "B", "C", "D", "E"],
            "description": "期望：都活 战斗应为：(A, A'BCD10), (E, E'1) 原因：10发都无敌"
        },
        {
            "name": "子弹合并双破防",
            "players": 3,
            "initial_ammo": 20,
            "operations": "AC1/BC1/CO",
            "expected_survivors": ["A", "B"],
            "description": "期望：AB活C死 战斗应为：(AB2, C防) 原因：同基础双破防"
        },
        {
            "name": "子弹合并核弹",
            "players": 3,
            "initial_ammo": 20,
            "operations": "AC1/BC2/CB1",
            "expected_survivors": ["A", "B"],
            "description": "期望：AB活C死 战斗应为：(AB3, C1) 原因：同基础核弹"
        },
        {
            "name": "2v1合并陷阱",
            "players": 3,
            "initial_ammo": 20,
            "operations": "AB2/B+/CA1",
            "expected_survivors": ["C"],
            "description": "期望：C活AB死 战斗应为：(A2, B), (C1, A) 原因：B是攻击者，不能把A（被攻击者）强行拉进来"
        },
        {
            "name": "N形问题",
            "players": 4,
            "initial_ammo": 20,
            "operations": "AC1D1/BC1/CA1B1/DA1",
            "expected_survivors": [],
            "description": "期望：都死 战斗应为：(AB2, CD2) 原因：综上"
        },
        {
            "name": "嘲讽测试",
            "players": 2,
            "initial_ammo": 20,
            "operations": "AB1/B-",
            "expected_survivors": ["A"],
            "description": "期望：A活B死 战斗应为：(A1, B嘲) 原因：嘲讽"
        },
        {
            "name": "多样化嘲讽测试",
            "players": 3,
            "initial_ammo": 20,
            "operations": "A-/B]/C]",
            "expected_survivors": ["A"],
            "description": "期望：A活BC死 原因：石头vs布"
        }
    ]

    passed = 0
    failed = 0

    for i, test_case in enumerate(test_cases, 1):
        print(f"\n测试 {i}: {test_case['name']}")
        print(f"描述: {test_case['description']}")

        try:
            game = Game(GameRules(allow_multiple_targets=True,
                                  enable_blasts=True,
                                  enable_self_attacks=True,
                                  enable_advanced_merging=True,
                                  enable_multiple_taunts=True))
            add_temp_players(game, test_case['players'], test_case['initial_ammo'])

            result = step_with_string(game, test_case['operations'])

            expected_players = {game.find_player_with_name(name) for name in test_case['expected_survivors']}
            actual_players = result.active_players

            if actual_players == expected_players:
                print(f"✓ 通过")
                passed += 1
            else:
                print(f"✗ 失败")
                print(f"  期望存活: {[p.name for p in expected_players]}")
                print(f"  实际存活: {[p.name for p in actual_players]}")
                failed += 1

        except Exception as e:
            print(f"✗ 异常: {e}")
            failed += 1

    print(f"\n=== 测试总结 ===")
    print(f"总计: {len(test_cases)}")
    print(f"通过: {passed}")
    print(f"失败: {failed}")
    print(f"成功率: {passed / len(test_cases) * 100:.1f}%")


def play_game():
    """在终端与2名AI玩家进行游戏"""
    print("=== 小兵小兵游戏 ===")
    print("规则：全部规则")
    print("玩家：P (你) vs 1 (AI) vs 2 (AI)")

    # 创建游戏，使用全部规则
    game = Game(ALL_RULES)

    # 添加玩家
    game.add_player("P")  # 人类玩家
    game.add_player("1", is_ai=True)  # AI玩家1
    game.add_player("2", is_ai=True)  # AI玩家2

    # 游戏主循环
    while not game.ending:
        print(f"\n=== 第{game.turn + 1}回合 ===")

        # 显示当前状态
        print("当前状态：")
        for player in sorted(game.active_players, key=lambda x: x.name):
            status = "活" if player.alive else "死"
            ammo = player.ammo
            last_op = player.operation.type.value if player.operation else "无"

            # 显示攻击目标的详细信息
            if player.operation and player.operation.type == OperationType.ATTACK and player.operation.attacks:
                attack_info = []
                for target, ammo_used in player.operation.attacks.items():
                    attack_info.append(f"{target.name}x{ammo_used}")
                last_op = f"攻击:{','.join(attack_info)}"

            print(f"{player.name}: {ammo}弹 {status} {last_op}")

        # 人类玩家操作
        if any(p.name == "P" and p.alive for p in game.active_players):
            print("\n请选择操作：")
            print("1: 装弹")
            print("2: 防御")
            print("3: 攻击")
            print("4: 嘲讽")
            print("5: yeah~嘲讽")
            print("6: 假5发嘲讽")

            try:
                choice = input("输入操作编号: ").strip()

                if choice == "1":
                    game.add_operation("P", Operation(OperationType.LOAD))
                elif choice == "2":
                    game.add_operation("P", Operation(OperationType.DEFENCE))
                elif choice == "3":
                    # 攻击操作需要选择目标和弹药
                    attacks = {}
                    while True:
                        print("\n选择攻击目标 (输入玩家名称，空行结束):")
                        print("可用目标:", [p.name for p in game.active_players if p.name != "P" and p.alive])
                        target_name = input("目标: ").strip()
                        if not target_name:
                            break

                        target = game.find_player_with_name(target_name)
                        if not target:
                            print("目标不存在!")
                            continue
                        if not target.alive:
                            print("目标已死亡!")
                            continue

                        try:
                            ammo_used = int(input(f"对{target_name}使用的弹药数: "))
                            current_ammo = next(p for p in game.active_players if p.name == "P").ammo
                            total_used = sum(attacks.values()) + ammo_used
                            if total_used > current_ammo:
                                print(f"弹药不足! 当前有{current_ammo}弹，已分配{sum(attacks.values())}弹")
                                continue

                            attacks[target] = ammo_used
                            print(f"已分配: {target_name}x{ammo_used}")

                        except ValueError:
                            print("请输入有效数字!")

                    if attacks:
                        game.add_operation("P", Operation(OperationType.ATTACK, attacks))
                    else:
                        print("未选择任何目标，改为装弹")
                        game.add_operation("P", Operation(OperationType.LOAD))

                elif choice == "4":
                    game.add_operation("P", Operation(OperationType.TAUNT))
                elif choice == "5":
                    game.add_operation("P", Operation(OperationType.TAUNT_YEAH))
                elif choice == "6":
                    game.add_operation("P", Operation(OperationType.TAUNT_FAKE_5))
                else:
                    print("无效选择，默认装弹")
                    game.add_operation("P", Operation(OperationType.LOAD))

            except (EOFError, KeyboardInterrupt):
                print("\n游戏结束")
                return
        else:
            print("玩家P已死亡，跳过操作")

        # 执行回合
        print("\n执行回合...")
        game.step()

    # 游戏结束
    print(f"\n=== 游戏结束 ===")
    if game.winner:
        if isinstance(game.winner, Player):
            print(f"胜利者: {game.winner.name}")
        else:
            print(f"胜利者: {game.winner.name}")
    else:
        print("平局! 所有玩家都失败了")


if __name__ == "__main__":
    test()
    play_game()
